package top.swimmer.tools.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import top.swimmer.tools.exception.BaseExceptions;

import java.io.*;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utils.
 */
public final class Utils {
    private static final Pattern MOBILE_PATTERN = Pattern.compile("^\\d{11}$");

    private static final Pattern EMAIL_PATTERN =
            Pattern.compile("^([a-z0-9A-Z]+[_-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");

    private static final Pattern INTEGER_PATTERN = Pattern.compile("^[-\\+]?[\\d]*$");

    private Utils() {
        // private ctor
    }

    // ===== File ===== //
    public static String readFileContent(String fileName) {
        ClassLoader classLoader = Utils.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(fileName);

        StringBuilder result = new StringBuilder();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));) {
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line).append(getLineSeparator());
            }

            reader.close();
        } catch (IOException e) {
            throw new RuntimeException("Error occurred during read file [" + fileName + "].", e);
        }

        return result.toString();
    }

    public static void appendFile(File file, String content) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(new BufferedWriter(new FileWriter(file, true)));
            out.print(content);
        } catch (Exception e) {
            //ignore silently
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    public static File createFile(String fileName) {
        File file = new File(fileName);

        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        return file;
    }


    public static File createDir(String dirName) {
        File dir = new File(dirName);

        if (!dir.exists()) {
            dir.mkdir();
        }

        return dir;
    }


    public static String toJSON(Object input) {
        if (input == null)
            return null;

        return JSON.toJSONString(input, SerializerFeature.PrettyFormat);
    }

    public static String toJson(Object input, String[] filterProperties) {
        if (input == null)
            return null;

        SerializeWriter writer = new SerializeWriter();
        JSONSerializer serializer = new JSONSerializer(writer);

        // apply filters
        if (filterProperties != null) {
            final Set<String> filters = new HashSet(Arrays.asList(filterProperties));
            serializer.getPropertyFilters().add(new PropertyFilter() {
                public boolean apply(Object source, String name, Object value) {
                    return !filters.contains(name);
                }
            });
        }

        serializer.write(input);
        return writer.toString();
    }

    public static <T> T fromJson(String input, Class<T> clazz) {
        return JSON.parseObject(input, clazz);
    }

    // ===== Date =====//
    public static Date now() {
        return new Date();
    }

    public static Long nowTime() {
        return now().getTime();
    }

    public static Date parseDate(String source, DateFormat format) {
        if (source == null)
            return null;

        try {
            return format.parse(source);
        } catch (ParseException e) {
            // ignore silently
        }

        return null;
    }

    public static Date daysBefore(int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, -1 * days);

        return calendar.getTime();
    }

    public static Date daysBefore(Date time, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time.getTime());

        calendar.add(Calendar.DATE, -1 * days);

        return calendar.getTime();
    }

    public static Date daysAfter(int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, days);

        return calendar.getTime();
    }

    public static Date daysAfter(Date time, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time.getTime());

        calendar.add(Calendar.DATE, days);

        return calendar.getTime();
    }

    public static Date getDayStart(Date time){
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time.getTime());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    public static Date minutesBefore(int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, -1 * minutes);

        return calendar.getTime();
    }

    public static Date minutesBefore(Date time, int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time.getTime());

        calendar.add(Calendar.MINUTE, -1 * minutes);

        return calendar.getTime();
    }

    public static Date minutesAfter(int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(now().getTime());
        calendar.add(Calendar.MINUTE, minutes);

        return calendar.getTime();
    }

    public static Date minutesAfter(Date time, int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(time.getTime());

        calendar.add(Calendar.MINUTE, minutes);

        return calendar.getTime();
    }

    public static Date today() {
        Calendar calendar = new GregorianCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    // ===== Check ===== //
    public static boolean validateMobile(String mobile) {
        Matcher m = MOBILE_PATTERN.matcher(mobile);
        return m.matches();
    }

    public static boolean validateEmail(String email) {
        Matcher m = EMAIL_PATTERN.matcher(email);
        return m.matches();
    }

    public static boolean validateInteger(String value) {
        Matcher m = INTEGER_PATTERN.matcher(value);
        return m.matches();
    }

    public static <T> T checkNull(Object param, String paramName) {
        if (param == null) {
            throw BaseExceptions.INSTANCE.missingField(paramName);
        }

        return (T) param;
    }

    public static String checkString(String param, String paramName) {
        if (param == null || param.trim().length() == 0) {
            throw BaseExceptions.INSTANCE.missingField(paramName);
        }

        return param;
    }

    public static boolean equals(String a, String b) {
        if (a == null && b == null)
            return true;

        if ((a == null && b != null) || (a != null && b == null)) {
            return false;
        }

        return a.equalsIgnoreCase(b);
    }

    public static boolean equalsCaseSensitive(String a, String b) {
        if (a == null && b == null)
            return true;

        if ((a == null && b != null) || (a != null && b == null)) {
            return false;
        }

        return a.equals(b);
    }

    public static boolean equals(Integer a, Integer b) {
        if (a == null && b == null)
            return true;

        if (a == null || b == null) {
            return false;
        }

        return a.equals(b);
    }

    public static boolean isEmpty(String value) {
        return value == null || value.trim().length() == 0;
    }

    public static <T> boolean isCollectionEmpty(Collection<T> collection) {
        return collection == null || collection.isEmpty();
    }

    public static boolean isArrayEmpty(Object[] objects) {
        return objects == null || (objects != null && objects.length == 0);
    }

    public static String nullIfEmpty(String value) {
        return isEmpty(value) ? null : value;
    }

    public static <T> List<T> nullIfEmpty(List<T> collections) {
        return isCollectionEmpty(collections) ? null : collections;
    }

    public static String isNull(String input, String defaultValue) {
        return input == null ? defaultValue : input;
    }

    public static Integer isNull(Integer input, Integer defaultValue) {
        return input == null ? defaultValue : input;
    }

    // ===== Convert ===== //
    public static String str(Object obj) {
        return obj == null ? null : String.valueOf(obj);
    }

    public static Integer str2int(String input) {
        try {
            return isEmpty(input) ? null : Integer.valueOf(input);
        } catch (Exception e) {
            return null;
        }
    }

    public static Long str2long(String input) {
        return isEmpty(input) ? null : Long.valueOf(input);
    }

    public static Double str2double(String input) {
        return isEmpty(input) ? null : Double.valueOf(input);
    }

    public static BigDecimal str2decimal(String input) {
        return isEmpty(input) ? null : new BigDecimal(input);
    }

    // ===== Misc ===== //
    public static String find(Pattern pattern, String content) {
        Matcher matcher = pattern.matcher(content);

        if (matcher.find()) {
            String text = matcher.group(1);
            return text == null ? null : text.trim().replaceAll("&nbsp;", "");
        } else {
            return null;
        }
    }

    public static String find(Pattern pattern, String content, Integer index) {
        Matcher matcher = pattern.matcher(content);

        if (matcher.find()) {
            String text = matcher.group(index);
            return text == null ? null : text.trim().replaceAll("&nbsp;", "");
        } else {
            return null;
        }
    }

    public static String getLineSeparator() {
        return System.getProperty("line.separator");
    }

    public static String trim(String input) {
        if (input == null)
            return null;

        return input.trim();
    }


    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                // ignore silently
            }
        }
    }

    public static Class<?> guessComponentType(List list) {
        if (list == null)
            return null;

        for (Object element : list) {
            if (element == null)
                continue;

            return element.getClass();
        }

        return null;
    }
}
